package com.tang.kite.executor.defaults

import com.tang.kite.executor.Executor
import com.tang.kite.session.Configuration
import com.tang.kite.sql.SqlStatement
import com.tang.kite.transaction.Transaction
import com.tang.kite.utils.ResultSetHandlers
import org.slf4j.LoggerFactory
import java.sql.Connection
import java.sql.Statement

/**
 * @author Tang
 */
class DefaultExecutor(

    private val configuration: Configuration,

    private val transaction: Transaction

) : Executor {

    private val logger = LoggerFactory.getLogger(DefaultExecutor::class.java)

    override fun getConnection(): Connection {
        return transaction.getConnection()
    }

    override fun <T> count(statement: SqlStatement, type: Class<T>): Long {
        val connection = getConnection()
        val preparedStatement = connection.prepareStatement(statement.sql)
        statement.setValues(preparedStatement)
        return runCatching {
            val resultSet = preparedStatement.executeQuery()
            ResultSetHandlers.getCount(resultSet)
        }.onFailure {
            it.printStackTrace()
            connection.rollback()
        }.also {
            logger.debug("Closing count prepared statement [{}]", preparedStatement)
            preparedStatement.close()
        }.getOrDefault(0)
    }

    override fun <T> query(statement: SqlStatement, type: Class<T>): List<T> {
        val connection = getConnection()
        val preparedStatement = connection.prepareStatement(statement.sql)
        statement.setValues(preparedStatement)
        return runCatching {
            val resultSet = preparedStatement.executeQuery()
            ResultSetHandlers.getList(resultSet, type)
        }.onFailure {
            it.printStackTrace()
            connection.rollback()
        }.also {
            logger.debug("Closing query prepared statement [{}]", preparedStatement)
            preparedStatement.close()
        }.getOrDefault(emptyList())
    }

    override fun update(statement: SqlStatement, parameter: Any): Int {
        val connection = getConnection()
        val autoGeneratedKeys = if (ResultSetHandlers.hasGeneratedKey(statement, parameter)) {
            Statement.RETURN_GENERATED_KEYS
        } else {
            Statement.NO_GENERATED_KEYS
        }
        val preparedStatement = connection.prepareStatement(statement.sql, autoGeneratedKeys)
        statement.setValues(preparedStatement)
        return runCatching {
            preparedStatement.executeUpdate()
        }.onFailure {
            it.printStackTrace()
            connection.rollback()
        }.also {
            ResultSetHandlers.setGeneratedKey(statement, preparedStatement, parameter)
            logger.debug("Closing update prepared statement [{}]", preparedStatement)
            preparedStatement.close()
        }.getOrDefault(0)
    }

    override fun commit() {
        transaction.commit()
    }

    override fun rollback() {
        transaction.rollback()
    }

    override fun close() {
        transaction.close()
    }

}
